#pragma once

#include <queue>

template <class T>
class ConcurrentQueue
{
public:
  ConcurrentQueue()
  {
    InitializeCriticalSectionAndSpinCount(&mItemLockCS, 1000);
    InitializeConditionVariable(&mBufferNotEmptyCV);
  }

  ~ConcurrentQueue()
  {
    DeleteCriticalSection(&mItemLockCS);
  }

  void Push(const T& item)
  {
    EnterCriticalSection(&mItemLockCS);
    mItems.push(item);
    LeaveCriticalSection(&mItemLockCS);

    WakeConditionVariable(&mBufferNotEmptyCV);
  }

  T Pop()
  {
    T item = 0;

    EnterCriticalSection(&mItemLockCS);

    if (!mItems.empty())
    {
      item = mItems.front();
      mItems.pop();
    }

    LeaveCriticalSection(&mItemLockCS);

    return item;
  }

  T WaitAndPop()
  {
    EnterCriticalSection(&mItemLockCS);

    while( mItems.empty() )
    {
      SleepConditionVariableCS(&mBufferNotEmptyCV, &mItemLockCS, INFINITE);
    }

    T item = mItems.front();
    mItems.pop();

    LeaveCriticalSection(&mItemLockCS);

    return item;
  }

private:
  std::queue<T> mItems;
  CRITICAL_SECTION mItemLockCS;
  CONDITION_VARIABLE mBufferNotEmptyCV;
};
